package com.wang.diaryauth.runner;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.wang.diaryauth.domain.Permission;
import com.wang.diaryauth.domain.Role;
import com.wang.diaryauth.domain.RolePermissionRel;
import com.wang.diaryauth.mapper.PermissionMapper;
import com.wang.diaryauth.mapper.RoleMapper;
import com.wang.diaryauth.mapper.RolePermissionRelMapper;
import com.wang.framework.common.constant.RedisKeyConstants;
import com.wang.framework.common.util.JsonUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author : wangs
 * @date : 2024/11/18 13:35
 * @modyified By :
 */
@Component
@Slf4j
public class PushRolePermissions2RedisRunner implements ApplicationRunner {

    @Resource
    private RedisTemplate<String,String> redisTemplate;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private RolePermissionRelMapper rolePermissionRelMapper;

    @Resource
    private PermissionMapper permissionMapper;
    // 权限同步标记 Key
    private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("初始化角色信息开始。。。。");

        try {
            // 是否能够同步数据: 原子操作，只有在键 PUSH_PERMISSION_FLAG 不存在时，才会设置该键的值为 "1"，并设置过期时间为 1 天
            boolean canPushed = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS));

            // 如果无法同步权限数据
            if (!canPushed) {
                log.warn("==> 角色权限数据已经同步至 Redis 中，不再同步...");
                return;
            }
            // 查询所有启用的角色
            List<Role> roles = roleMapper.selectEnableList();

            if (CollUtil.isNotEmpty(roles)) {
                List<Long> rolesIds = roles.stream().map(Role::getId).collect(Collectors.toList());
                // 查询角色与权限的关系
                List<RolePermissionRel> rolePermissionRels = rolePermissionRelMapper.selectByRoleIds(rolesIds);
                // 按角色 ID 分组, 每个角色 ID 对应多个权限 ID
                Map<Long, List<Long>> roleIdPermissionIdsMap = rolePermissionRels.stream().collect(
                        Collectors.groupingBy(RolePermissionRel::getRoleId,
                                Collectors.mapping(RolePermissionRel::getPermissionId, Collectors.toList()))
                );
                // 查询所有启用的权限
                List<Permission> permissions = permissionMapper.selectAppEnabledList();
                Map<Long, Permission> permissionIdsMap = permissions.stream()
                        .collect(Collectors.toMap(Permission::getId, permission -> permission));
                //组成角色权限集合
                HashMap<Long, List<Permission>> map = new HashMap<>();

                roles.forEach(role -> {
                    Long id = role.getId();
                    List<Long> permissionIds = roleIdPermissionIdsMap.get(id);
                    if (CollUtil.isNotEmpty(permissionIds)) {
                        List<Permission> permissionList = Lists.newArrayList();
                        permissionIds.forEach(permissionId->{
                            Permission permission = permissionIdsMap.get(permissionId);
                            permissionList.add(permission);
                        });
                        map.put(id,permissionList);
                    }
                });
                //同步到redis
                roleIdPermissionIdsMap.forEach((roleId,permission)->{
                    String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
                    redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permission));
                });
            }
            log.info("初始化角色信息完成。。。。");
        }
        catch (Exception e){
            log.info("初始化角色信息失败。。。。");
        }


    }
}
